package com.myk.game.gobangchess.rooms;

import com.myk.game.gobangchess.constants.BoardStyleData;
import com.myk.game.gobangchess.DataManager;
import com.myk.game.gobangchess.Room;
import com.myk.game.gobangchess.constants.WinMode;
import com.myk.game.gobangchess.signs.AroundSign;
import com.myk.game.gobangchess.signs.TextSign;

import java.util.*;

public class GravityFourRoom extends Room {

    private static final int millisecondForAnimation = 300;

    enum Direction {
        UNKNOWN, UP, DOWN, LEFT, RIGHT
    }

    private Direction curGravity = Direction.DOWN;
    private boolean afterChangeGravity = false;

    public GravityFourRoom(String roomName) {
        super(roomName, "\t\t\t\t能够改变引力方向的四子棋，棋子连四子获胜，连五子不行，双方不能连续改变引力，夏日设计。", "引力四子棋",BoardStyleData.FOUR_GRAVITE_STYLE_6x6, true, true);
    }

    @Override
    public String getRuleText() {
        return "黑白双方轮流行动：\n" +
                "选择A：落子（棋子会向下跌落）\n" +
                "选择B：改变引力方向（所有棋子向新引力方向跌落）\n" +
                "\n" +
                "限制：一方改完引力方向后，对方下一手不得立刻再次改变引力，必须落子\n" +
                "获胜条件：行动后，形成己方四子一线\n" +
                "特殊：五子一线不算赢，对方继续行动";
    }

    @Override
    public String getCoordMode() {
        return "33332222";
    }

    @Override
    protected void onStartPrepare() {
        this.curGravity = Direction.DOWN; //初始默认引力向下
        this.updateGravity(curGravity); //为了显示墙的方向
        this.afterChangeGravity = false; //初始第一回合可以修改引力
        noticeBlackMove(); //通知黑方先行
    }

    @Override
    public void doRule(int row, int col, int value) {
        // 改变引力
        if (!this.isInGrids(row, col)) {
            // 上回修改了引力
            if (afterChangeGravity) {
                return;
            }

            if ((row < gridRowMin() || row > gridRowMax()) && (col < gridRowMin() || col > gridRowMax())) {
                // 忽略四个角落
                return;
            }

            Direction selectDirection;
            if (row < gridRowMin()) {
                selectDirection = Direction.UP;
            } else if (row > gridRowMax()) {
                selectDirection = Direction.DOWN;
            } else if (col < gridRowMin()) {
                selectDirection = Direction.LEFT;
            } else if (col > gridRowMax()) {
                selectDirection = Direction.RIGHT;
            } else {
                // 应该是不会到这儿的
                //System.out.println("判断方向出错啦！");
                return;
            }

            // 并没有改变引力
            if (this.curGravity == selectDirection) {
                return;
            }

            signList.clear();

            // 修改引力
            this.afterChangeGravity = true; //修改引力后不能再接着修改
            this.updateGravity(selectDirection);
        } else if (curGrids[row][col] == 0) {
            signList.clear();
            signList.add(new AroundSign(getIndex(row, col), "#00000000", "#6600FF00", 1.2f));
            curGrids[row][col] = value; //实际落子
            this.afterChangeGravity = false; //实际落子后可以再修改引力
        } else {
            // 下的位置已经有棋子了
            return;
        }

        noticeNoMove();

        new Timer().schedule(new TimerTask() {
            @Override
            public void run() {
                // 刷新棋盘
                updateBoard();
                noticeAllRefreshGameInfo(); //走完后要刷新一下，否则大家看不到最终结局
                if (!checkWin(value)) {
                    if (value == 1) {
                        stepAdd();
                        noticeWhiteMove();
                    } else if (value == 2) {
                        stepAdd();
                        noticeBlackMove();
                    }
                }
            }
        }, millisecondForAnimation);

    }

    // 棋盘row能达到的小边界
    protected int gridRowMin() {
        return 2;
    }

    // 棋盘row能达到的大边界
    protected int gridRowMax() {
        return curGrids.length - 3;
    }

    // 棋盘col能达到的小边界
    protected int gridColMin() {
        return 2;
    }

    // 棋盘col能达到的大边界
    protected int gridColMax() {
        return curGrids[0].length - 3;
    }

    // 判断位置在棋盘上
    private boolean isInGrids(int row, int col) {
        return row >= gridRowMin() && row <= gridRowMax() && col >= gridColMin() && col <= gridColMax();
    }

    // 交换棋盘上两个棋子的位置
    private void swapInGrids(int r1, int c1, int r2, int c2) {
        int tmp = curGrids[r1][c1];
        curGrids[r1][c1] = curGrids[r2][c2];
        curGrids[r2][c2] = tmp;
    }

    // 根据 this.curGravity 更新棋盘
    private void updateBoard() {
        switch (this.curGravity) {
            case DOWN:
                for (int col = gridColMin(); col <= gridColMax(); col++) {
                    int p1 = gridRowMax();
                    int p2 = p1;
                    while (p2 >= gridRowMin()) {
                        if (curGrids[p2][col] != 0) {
                            this.swapInGrids(p1, col, p2, col);
                            p1--;
                        }
                        p2--;
                    }
                }
                break;
            case UP:
                for (int col = gridColMin(); col <= gridColMax(); col++) {
                    int p1 = gridRowMin();
                    int p2 = p1;
                    while (p2 <= gridRowMax()) {
                        if (curGrids[p2][col] != 0) {
                            this.swapInGrids(p1, col, p2, col);
                            p1++;
                        }
                        p2++;
                    }
                }
                break;
            case LEFT:
                for (int row = gridRowMin(); row <= gridRowMax(); row++) {
                    int p1 = gridColMin();
                    int p2 = p1;
                    while (p2 <= gridColMax()) {
                        if (curGrids[row][p2] != 0) {
                            this.swapInGrids(row, p1, row, p2);
                            p1++;
                        }
                        p2++;
                    }
                }
                break;
            case RIGHT:
                for (int row = gridRowMin(); row <= gridRowMax(); row++) {
                    int p1 = gridColMax();
                    int p2 = p1;
                    while (p2 >= gridColMin()) {
                        if (curGrids[row][p2] != 0) {
                            this.swapInGrids(row, p1, row, p2);
                            p1--;
                        }
                        p2--;
                    }
                }
                break;
            default:
                // 应该是不会到这儿的
                //System.out.println("更新棋盘时方向出错啦！");
        }
    }

    private void updateGravity(Direction d) {
        Set<Integer> StopSet = new HashSet<>();
        for (int i = 0; i < curGrids.length; i++) {
            curGrids[i][0] = 0;
            curGrids[i][curGrids[0].length - 1] = 0;
            StopSet.add(getIndex(i, 0));
            StopSet.add(getIndex(i, curGrids[0].length - 1));
        }
        for (int i = 0; i < curGrids[0].length; i++) {
            curGrids[0][i] = 0;
            curGrids[curGrids.length - 1][i] = 0;
            StopSet.add(getIndex(0, i));
            StopSet.add(getIndex(curGrids[0].length - 1, i));
        }
        Set<Integer> PutChessSet = new HashSet<>();
        switch (d) {
            case DOWN:
                for (int i = 0; i < curGrids[0].length; i++) {
                    curGrids[curGrids.length - 1][i] = 9;
                    PutChessSet.add(getIndex(curGrids.length - 1, i));
                    StopSet.remove(getIndex(curGrids.length - 1, i));
                }
                break;
            case UP:
                for (int i = 0; i < curGrids[0].length; i++) {
                    curGrids[0][i] = 9;
                    PutChessSet.add(getIndex(0, i));
                    StopSet.remove(getIndex(0, i));
                }
                break;
            case LEFT:
                for (int i = 0; i < curGrids.length; i++) {
                    curGrids[i][0] = 9;
                    PutChessSet.add(getIndex(i, 0));
                    StopSet.remove(getIndex(i, 0));
                }
                break;
            case RIGHT:
                for (int i = 0; i < curGrids.length; i++) {
                    curGrids[i][curGrids[0].length - 1] = 9;
                    PutChessSet.add(getIndex(i, curGrids[0].length - 1));
                    StopSet.remove(getIndex(i, curGrids[0].length - 1));
                }
                break;
            default:
                // 应该是不会到这儿的
                //System.out.println("更新引力方向时出错啦！");
                return;
        }
        this.curGravity = d;
        if (this.afterChangeGravity) {
            signList.add(new AroundSign(new ArrayList<>(PutChessSet), "#00000000", "#6600FF00", 1.2f));
            for (int index : StopSet) {
                signList.add(new TextSign(index, "×", "#66FF0000"));
            }
        }
    }

    protected int getJudgeLength(int x, int y, int dx, int dy) {
        int value = curGrids[x][y];
        int length = 0;
        while (isInGrids(x, y)) {
            if (curGrids[x][y] != value) {
                return length;
            }
            length++;
            x += dx;
            y += dy;
        }
        return length;
    }

    public boolean checkWin(int value) {
        boolean bH4 = false;
        boolean wH4 = false;
        boolean gridFull = true;
        for (int dx = -1; dx <= 1; dx++) {
            for (int dy = -1; dy <= 1; dy++) {
                if (dx == 0 && dy == 0) {
                    continue;
                }
                for (int x = gridRowMin(); x <= gridRowMax(); x++) {
                    for (int y = gridColMin(); y <= gridColMax(); y++) {
                        if (curGrids[x][y] == 0) {
                            gridFull = false;
                        }
                        int len = getJudgeLength(x, y, dx, dy) + getJudgeLength(x, y, -dx, -dy) - 1;
                        if (len == 4) {
                            if (curGrids[x][y] == 1) {
                                bH4 = true;
                            } else if (curGrids[x][y] == 2) {
                                wH4 = true;
                            }
                        }
                    }
                }
            }
        }

        if (bH4 && wH4){
            if (value == 1){
                doOver(WinMode.BLACK_WIN, player_black.getNickNameOrTemp() + "执黑棋战胜" + player_white.getNickNameOrTemp());
            }
            else {
                doOver(WinMode.WHITE_WIN, player_white.getNickNameOrTemp() + "执白棋战胜" + player_black.getNickNameOrTemp());
            }
        }
        else if (bH4){
            doOver(WinMode.BLACK_WIN, player_black.getNickNameOrTemp() + "执黑棋战胜" + player_white.getNickNameOrTemp());
        }
        else if (wH4){
            doOver(WinMode.WHITE_WIN, player_white.getNickNameOrTemp() + "执白棋战胜" + player_black.getNickNameOrTemp());
        }
        else if (gridFull){
            doOver(WinMode.BOTH_DRAW, player_black.getNickNameOrTemp() + "-黑棋战平" + player_white.getNickNameOrTemp() + "-白棋");
        }
        else {
            return false;
        }
        return true;
    }

}
